package com.ruoyi.framework.config;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.*;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * @program: RuoYi
 * @description: redis缓存配置: 重载redisCacheManager实现过期时间可配置
 * @author: yanghua
 * @create: 2021-10-12 10:48
 **/
@Configuration
@EnableTransactionManagement
@CacheConfig
@EnableCaching
public class RedisCacheConfig {
//    /**
//     * 首先redis默认的key和value序列化方式不太方便在工具类中展示所以使用自定义的序列化方式处理key和value值
//     * FastjsonRedisSerializer 是自定义的序列化方式类,通过fastJson处理
//     * RedisConnectionFactory 是springboot默认通过加载配置给初始化的
//     * @param factory
//     * @return
//     */
//    @Bean
//    public RedisTemplate<String, String> redisTemplate(@Autowired RedisConnectionFactory factory) {
//        StringRedisTemplate template = new StringRedisTemplate(factory);
//        template.setKeySerializer(new StringRedisSerializer());//设置redis中缓存的key 的序列化/反序列化方式
//        template.setValueSerializer(new FastjsonRedisSerializer());//设置redis 中value 的序列化/反序列化方式
//        template.afterPropertiesSet();
//        return template;
//    }
//
//    /**
//     * 这里配置的是RedisCacheManager 实现@Cacheable注解内容的cacheManager 属性
//     * 指定缓存时指定到redis中 还是我们本地的内存中 这个里面配置的是redis中的配置
//     * 里面的Map中配置的是各个key 值缓存的时间,然后默认的key配置时间
//     * @param redisConnectionFactory
//     * @return
//     */
//    @Bean("redisCacheManager")
//    @Primary
//    public RedisCacheManager initRedisCacheManager(@Autowired RedisTemplate redisTemplate){
//        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//        Map<String, Long> expires = new HashMap<>();
//        expires.put("user",1000L);//设置redis中指定key 的缓存时间
//        expires.put("khy",1000L);//设置redis中指定key 的缓存时间
//        //.... 每个key 都可以在这里设置
//        redisCacheManager.setExpires(expires);
//        redisCacheManager.setDefaultExpiration(1500);//设置默认的key缓存失效时间
//        return redisCacheManager;
//    }


    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //初始化一个RedisCacheConfiguration
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        //返回一个自定义的CacheManager
        return new CustomizeTtlRedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }

}

/**
 * @Description: 重载redisCacheManager-从cacheName中提取过期时间进行配置
 **/
class CustomizeTtlRedisCacheManager extends RedisCacheManager {

    public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }

    public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
    }

    public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
        super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
    }

    public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
    }

    public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
    }

    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
        Duration ttl = getTtlByName(name);
        if (ttl != null) {
            //证明在cacheName上使用了过期时间，需要修改配置中的ttl
            cacheConfig = cacheConfig.entryTtl(ttl);
        }
        //修改缓存key和value值的序列化方式
        cacheConfig = cacheConfig.computePrefixWith(DEFAULT_CACHE_KEY_PREFIX)
                .serializeValuesWith(DEFAULT_PAIR);
        return super.createRedisCache(name, cacheConfig);
    }

    /**
     * 缓存参数的分隔符
     * 数组元素0=缓存的名称
     * 数组元素1=缓存过期时间TTL
     */
    private static final String DEFAULT_SEPARATOR = "#";

    /**
     * 通过name获取过期时间
     *
     * @param name
     * @return
     */
    private Duration getTtlByName(String name) {
        if (name == null) {
            return null;
        }
        //根据分隔符拆分字符串，并进行过期时间ttl的解析
        String[] cacheParams = name.split(DEFAULT_SEPARATOR);
        if (cacheParams.length > 1) {
            String ttl = cacheParams[1];
            if (!StringUtils.isEmpty(ttl)) {
                try {
                    return Duration.ofSeconds(Long.parseLong(ttl));
                } catch (Exception e) {
                    // TODO
                }
            }
        }else {//默认key存活时间
            try {
                return Duration.ofSeconds(300L);
            } catch (Exception e) {
                // TODO
            }
        }
        return null;
    }

    /**
     * 默认的key前缀
     */
    private static final CacheKeyPrefix DEFAULT_CACHE_KEY_PREFIX = cacheName -> cacheName + ":";

    /**
     * 默认序列化方式为json
     */
    private static final RedisSerializationContext.SerializationPair<Object> DEFAULT_PAIR = RedisSerializationContext.SerializationPair
            .fromSerializer(new GenericJackson2JsonRedisSerializer());
}
